// geometry_test.cc

#include <cassert>
#include <cstdio>
#include <set>
#include "geometry.h"

using namespace std;

// Ensure that each object in a sizable region has the proper number
// of neighbors of each type.
void RightNumberOfNeighbors() {
  for(int x = 0; x < 10; x++)
    for(int y = 0; y < 10; y++) {
      if (Geometry::IsTile(x, y)) {
	assert(Geometry::NumAdjTiles(x, y) == 6);
	assert(Geometry::NumAdjEdges(x, y) == 6);
	assert(Geometry::NumAdjNodes(x, y) == 6);
      }
      if (Geometry::IsEdge(x, y)) {
	assert(Geometry::NumAdjTiles(x, y) == 2);
	assert(Geometry::NumAdjEdges(x, y) == 4);
	assert(Geometry::NumAdjNodes(x, y) == 2);
      }
      if (Geometry::IsNode(x, y)) {
	assert(Geometry::NumAdjTiles(x, y) == 3);
	assert(Geometry::NumAdjEdges(x, y) == 3);
	assert(Geometry::NumAdjNodes(x, y) == 3);
      }
    }
}

// Build a world consisting of all the hexagons within distance 2 of a
// central hexagon and all affiliated edges and nodes.  Ensure that we
// end up with the right number of everything.
void BuildMiniWorldRec(int x, int y, int r,
		       set<pair<int, int> > &tiles,
		       set<pair<int, int> > &edges,
		       set<pair<int, int> > &nodes) {
  assert(Geometry::IsTile(x, y));

  // Add this tile together with its edges and nodes to the miniworld.
  tiles.insert(make_pair(x, y));
  for(int i = 0; i < Geometry::NumAdjEdges(x, y); i++) {
    int xp, yp;
    Geometry::AdjEdge(x, y, i, &xp, &yp);
    edges.insert(make_pair(xp, yp));
  }
  for(int i = 0; i < Geometry::NumAdjNodes(x, y); i++) {
    int xp, yp;
    Geometry::AdjNode(x, y, i, &xp, &yp);
    nodes.insert(make_pair(xp, yp));
  }

  // Recursively add adjacent tiles.
  if (r > 0) {
    for(int i = 0; i < Geometry::NumAdjTiles(x, y); i++) {
      int xp, yp;
      Geometry::AdjTile(x, y, i, &xp, &yp);
      BuildMiniWorldRec(xp, yp, r - 1, tiles, edges, nodes);
    }
  }
}

void BuildMiniWorld() {
  set<pair<int, int> > tiles;
  set<pair<int, int> > edges;
  set<pair<int, int> > nodes;
  
  BuildMiniWorldRec(1, 2, 2, tiles, edges, nodes);

  // The objective is to verify that the miniworld has the correct
  // number of tiles, edges, and nodes.  There is 1 row of 5 hexagonal
  // tiles, there are 2 rows of 4, and there are 2 rows of 3.  This
  // gives 1 * 5 + 2 * 4 + 2 * 3 = 19 tiles in all.

  assert(tiles.size() == 19);

  // Each tile has 6 edges for a total of 19 * 6 = 114.  However, this
  // counts interior edges twice (because the are shared by two tiles)
  // and edges on the periphery of the miniworld only once:
  //
  //          2 i + p = 114
  //
  // Let's determine p, the number of peripheral edges.  The miniworld
  // has 6 corner tiles with 3 peripheral edges each and 6 edge tiles
  // with 2 peripheral edges each, for a total of p = 6 * 3 + 6 * 2 =
  // 30 peripheral edges.  Plugging into the equation above, there are
  // i = (114 - 30) / 2 = 42 interior edges.  In all, there are p + i
  // = 30 + 42 = 72 edges.

  assert(edges.size() == 72);

  // Finally, each tile has 6 corners for a total of 19 * 6 = 114
  // nodes.  However, this doubly and triply counts some:
  //
  //          3 a + 2 b + c = 114
  //
  // There are 6 * 2 = 12 singly-counted nodes at each "corner" of the
  // outer ring and another 6 singly-counted nodes along edge edge,
  // for a total of c = 18.  In addition, there is a doubly-counted
  // node where pairs of tiles meet in the outer ring, which contains
  // 12 tiles; thus, there are b = 12 doubly counted nodes.  Plugging
  // into the equation and solving gives a = (114 - 2 * 12 - 1 * 18) /
  // 3 = 24.  Therefore, the total number of distinct nodes is a + b +
  // c = 24 + 12 + 18 = 54.

  assert(nodes.size() == 54);
}

void OriginTileExists() {
  int tx, ty;
  Geometry::GetOriginTile(&tx, &ty);
  assert(Geometry::IsTile(tx, ty));
}

int main(int argc, char **argv) {
  Geometry::Init();

  RightNumberOfNeighbors();
  BuildMiniWorld();
  OriginTileExists();

  puts("All tests passed.");
}
